import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.awt.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;

/**
 * This class represents the SalesWorld.
 * <br /><br />
 * It is responsible for handling the main game logic.
 * 
 * @author (Dilyan GEORGIEV)
 * @author (Thomas KOBER) 
 * @author (Matthew MCDONALD)
 * @version (1.0)
 */
public class SalesWorld extends World implements GameObserver
{
    ////////////////////////////////////////////////////////////////////////////////
    // Defaults
    ////////////////////////////////////////////////////////////////////////////////
    
    // Default Game Speed
    private static final int GAME_SPEED         = 33;
    // Default Timer Fire Interval
    private static final int DFLT_TIMER_DELAY   = 1000;
    // Default Score Base
    private static final int SCORE_BASE         = 150;
    // Default Time Base
    private static final int TIME_BASE          = 15;
    // Number of Levels
    private static final int LEVEL_COUNT        = 2;
    
    ////////////////////////////////////////////////////////////////////////////////
    // Field declarations
    ////////////////////////////////////////////////////////////////////////////////
    
    // The Salesman
    private Salesman            theSalesman;
    // The underlying Data Structure
    private SpaceTree<Planet>   spaceTree;
    // Current Cost Display
    private GameStatus          costStatus;
    // Current Time Display
    private GameStatus          timeStatus;
    // Game Timer
    private Timer               theTimer;
    // Game Timer Display
    private int                 timeElapsed;
    // Player Score
    private int                 score;
    // Control Variable to "softly" pause execution
    private boolean             paused;
    // Selected Difficulty
    private Difficulty          difficulty;
    // The Mean Traversing Machine
    private HAL                 hal;
    // Its Score
    private int                 halScore;

    ////////////////////////////////////////////////////////////////////////////////
    // Constructor(s)
    ////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Constructor for objects of class SalesWorld.
     * 
     */
    public SalesWorld()
    {    
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(800, 600, 1); 

        Greenfoot.setSpeed(GAME_SPEED);
        
        this.timeElapsed = -1;
        this.paused = true;
        
        this.theTimer = new Timer(DFLT_TIMER_DELAY, new ActionListener() {
            
            public void actionPerformed(ActionEvent evt)
            {
                updateGameTime();
            }
        
        });
        
        this.difficulty = Difficulty.FAIL;
        
        this.hal = new HAL();
        
        this.halScore = Integer.MAX_VALUE;
        
        this.theSalesman = Salesman.getInstance();
        this.theSalesman.addObserver(this);
        
        this.createRandomWorld();
        
        this.updateGameStatus();
        this.updateGameTime();
        this.displayWelcomeMessage();
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Public Implementation
    ////////////////////////////////////////////////////////////////////////////////

    /**
     * Returns whether or not the current game is paused.
     * @return true if the current game is paused, false otherwise.
     */
    public boolean isPaused()
    {
        return this.paused;
    }
    
    /**
     * Pauses or unpauses the current game.
     * @param paused if true, the current game is paused, if false, the current game is continued.
     */
    public void setPaused(boolean paused)
    {
        this.paused = paused;
    }
    
    /**
     * Returns the Worlds underlying data structure.
     * @return the Worlds underlying data structure.
     */
    public SpaceTree getSpaceTree()
    {
        return this.spaceTree;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // GameObserver Interface Implementation
    ////////////////////////////////////////////////////////////////////////////////
    
    // Unfortunately it is not possible to use the Java Observable/Observer construct in Greenfoot, as Observable is a class and needs to be subclassed
    
    /**
     * Handles the Salesman visiting a new Planet.
     */
    public void handleCostIncrease()
    {
        this.updateGameStatus();
    }
    
    /**
     * Handles the Players controlling the Salesman.
     */
    public void handlePlayerMoved()
    {
        this.checkTimerStarted();
    }
    
    /**
     * Handles the Player reaching a Goal Planet.
     */
    public void handlePlayerReachedGoalPlanet()
    {
        this.theTimer.stop();
        this.calcScore();
        this.paused = true;
        
        this.halScore = hal.calculatePath(this.spaceTree, this.difficulty);
        
        this.displayGameEndMessage();
    }
    
    /**
     * Handles the Player selecting a Game Difficulty.
     */
    public void handleDifficultySelected(Difficulty difficulty)
    {
        this.setDifficulty(difficulty);
        
        this.removeMessageBoxes();
        
        this.paused = false;
    }
    
    /**
     * Handles the Player selecting the play again option.
     */
    public void handlePlayAgainSelected()
    {
        this.resetWorld();
        
        this.paused = false;
    }
    
    /**
     * Handles the Player don't wanting to play once more.
     */
    public void handleGiveUpSelected()
    {
        this.resetWorld();
        
        Greenfoot.stop();
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Private Implementation
    ////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Creates a new, randomly chosen, World.
     */
    private void createRandomWorld()
    {
        int randomWorld = Greenfoot.getRandomNumber(LEVEL_COUNT);
        
        switch (randomWorld) {
            
            case 0:
                this.createWorld0();
                break;
                
            case 1:
                this.createWorld1();
                break;
                
            default:
                this.createWorld1();
                break;
            
        }
    }
    
    /**
     * Prepare the world for the start of the program. That is: create the initial
     * objects and add them to the world.
     * <br /><br />
     * This is World #0.
     */
    private void createWorld0()
    {   // Add Salesman
        super.addObject(theSalesman, 20, 255);
        
        // Add Root or Tree
        Planet pluto = new Pluto();
        pluto.setCost(new Cost0());
        super.addObject(pluto, 26, 300);
        
        // Arrow to right child
        Arrow arrow = new Arrow();
        arrow.getImage().rotate(-55);
        super.addObject(arrow, 56, 248);
        
        // Arrow to left child
        Arrow arrow2 = new Arrow();
        arrow2.getImage().rotate(55);
        super.addObject(arrow2, 53, 348);
        
        this.spaceTree = new SpaceTree<Planet>(pluto);
        SpaceNode leftTemp = this.spaceTree.getRoot();
        SpaceNode rightTemp = this.spaceTree.getRoot();
        
        this.theSalesman.setCurrentPlanet(this.spaceTree.getRoot());
        
        // Add 1st Tree Level
        Planet mercury = new Mercury();
        super.addObject(mercury, 116, 161);
        Cost cost3 = new Cost3();
        super.addObject(cost3, 87, 164);
        mercury.setCost(cost3);
        rightTemp = this.spaceTree.insertRight(rightTemp, mercury);
        
        // Arrow to right child
        Arrow arrow3 = new Arrow();
        arrow3.getImage().rotate(-45);
        super.addObject(arrow3, 148, 135);
        
        // Arrow to left child
        Arrow arrow4 = new Arrow();
        arrow4.getImage().rotate(30);
        super.addObject(arrow4, 154, 173);
        
        Planet mercury2 = new Mercury();
        super.addObject(mercury2, 112, 433);
        Cost cost1 = new Cost1();
        super.addObject(cost1, 84, 434);
        mercury2.setCost(cost1);
        leftTemp = this.spaceTree.insertLeft(leftTemp, mercury2);
        
        // Arrow to right child
        Arrow arrow5 = new Arrow();
        arrow5.getImage().rotate(-35);
        super.addObject(arrow5, 143, 408);
        
        // Arrow to left child
        Arrow arrow6 = new Arrow();
        arrow6.getImage().rotate(45);
        super.addObject(arrow6, 139, 461);
        
        // Add 2nd Tree Level
        Planet mars3 = new Mars();
        super.addObject(mars3, 220, 86);
        Cost cost62 = new Cost6();
        super.addObject(cost62, 188, 77);
        mars3.setCost(cost62);
        this.spaceTree.insertRight(rightTemp, mars3);
        
        // Add Arrow to right child
        Arrow arrow7 = new Arrow();
        arrow7.getImage().rotate(-25);
        super.addObject(arrow7, 264, 72);
        
        // Add Arrow to left child
        Arrow arrow8 = new Arrow();
        arrow8.getImage().rotate(20);
        super.addObject(arrow8, 264, 103);
        
        Planet mars4 = new Mars();
        super.addObject(mars4, 220, 200);
        Cost cost6 = new Cost6();
        super.addObject(cost6, 188, 190);
        mars4.setCost(cost6);
        this.spaceTree.insertLeft(rightTemp, mars4);
        
        // Add Arrow to left child
        Arrow arrow9 = new Arrow();
        arrow9.getImage().rotate(15);
        super.addObject(arrow9, 262, 211);
        
        Planet mars2 = new Mars();
        super.addObject(mars2, 220, 367);
        Cost4 cost43 = new Cost4();
        super.addObject(cost43, 188, 362);
        mars2.setCost(cost43);
        this.spaceTree.insertRight(leftTemp, mars2);
        
        // Add Arrow to right child
        Arrow arrow10 = new Arrow();
        arrow10.getImage().rotate(-15);
        super.addObject(arrow10, 260, 355);
        
        // Add Arrow to left child
        Arrow arrow11 = new Arrow();
        arrow11.getImage().rotate(20);
        super.addObject(arrow11, 260, 390);
        
        Planet mars = new Mars();
        super.addObject(mars, 220, 510);
        Cost cost8 = new Cost8();
        super.addObject(cost8, 188, 501);
        mars.setCost(cost8);
        this.spaceTree.insertLeft(leftTemp, mars);
        
        // Add Arrow to left child
        Arrow arrow12 = new Arrow();
        arrow12.getImage().rotate(15);
        super.addObject(arrow12, 261, 514);
        
        // Add 3rd Tree Level
        Planet moon = new Moon();
        super.addObject(moon, 415, 40);
        Cost cost42 = new Cost4();
        super.addObject(cost42, 390, 37);
        moon.setCost(cost42);
        this.spaceTree.insertRight(rightTemp.getRight(), moon);
        
        Planet moon2 = new Moon();
        super.addObject(moon2, 415, 140);
        Cost cost72 = new Cost7();
        super.addObject(cost72, 390, 137);
        moon2.setCost(cost72);
        this.spaceTree.insertLeft(rightTemp.getRight(), moon2);
        
        // Add Arrow to right child
        Arrow arrow13 = new Arrow();
        arrow13.getImage().rotate(-25);
        super.addObject(arrow13, 449, 126);
        
        // Add Arrow to right child
        Arrow arrow14 = new Arrow();
        arrow14.getImage().rotate(15);
        super.addObject(arrow14, 452, 155);
        
        Planet moon5 = new Moon();
        super.addObject(moon5, 415, 240);
        Cost cost12 = new Cost1();
        super.addObject(cost12, 388, 237);
        moon5.setCost(cost12);
        this.spaceTree.insertLeft(rightTemp.getLeft(), moon5);
        
        Planet moon3 = new Moon();
        super.addObject(moon3, 415, 340);
        Cost cost32 = new Cost3();
        super.addObject(cost32, 388, 337);
        moon3.setCost(cost32);
        this.spaceTree.insertRight(leftTemp.getRight(), moon3);
        
        // Add Arrow to left child
        Arrow arrow15 = new Arrow();
        arrow15.getImage().rotate(-25);
        super.addObject(arrow15, 455, 331);
        
        Planet moon6 = new Moon();
        super.addObject(moon6, 415, 440);
        Cost cost2 = new Cost2();
        super.addObject(cost2, 388, 437);
        moon6.setCost(cost2);
        this.spaceTree.insertLeft(leftTemp.getRight(), moon6);
        
        // Add Arrow to right child
        Arrow arrow16 = new Arrow();
        arrow16.getImage().rotate(-15);
        super.addObject(arrow16, 446, 424);
        
        // Add Arrow to left child
        Arrow arrow17 = new Arrow();
        arrow17.getImage().rotate(30);
        super.addObject(arrow17, 452, 454);
        
        Planet moon4 = new Moon();
        super.addObject(moon4, 415, 540);
        Cost cost53 = new Cost5();
        super.addObject(cost53, 388, 537);
        moon4.setCost(cost53);
        this.spaceTree.insertLeft(leftTemp.getLeft(), moon4);
        
        // ReMap temp pointers
        rightTemp = rightTemp.getRight().getLeft();
        leftTemp = leftTemp.getRight();
        
        // Add 4th Tree Level
        Planet neptune = new Neptune();
        super.addObject(neptune, 580, 85);
        Cost cost9 = new Cost9();
        super.addObject(cost9, 555, 80);
        neptune.setCost(cost9);
        this.spaceTree.insertRight(rightTemp, neptune);
        
        Planet neptune2 = new Neptune();
        super.addObject(neptune2, 580, 185);
        Cost cost82 = new Cost8();
        super.addObject(cost82, 555, 180);
        neptune2.setCost(cost82);
        this.spaceTree.insertLeft(rightTemp, neptune2);
        
        // Add Arrow to right child
        Arrow arrow18 = new Arrow();
        arrow18.getImage().rotate(-25);
        super.addObject(arrow18, 617, 172);
        
        Planet neptune3 = new Neptune();
        super.addObject(neptune3, 580, 300);
        Cost cost22 = new Cost2();
        super.addObject(cost22, 555, 295);
        neptune3.setCost(cost22);
        this.spaceTree.insertRight(leftTemp.getRight(), neptune3);
        
        // Add Arrow to left child
        Arrow arrow19 = new Arrow();
        arrow19.getImage().rotate(20);
        super.addObject(arrow19, 617, 316);
        
        Planet neptune4 = new Neptune();
        addObject(neptune4, 580, 405);
        Cost cost52 = new Cost5();
        super.addObject(cost52, 555, 400);
        neptune4.setCost(cost52);
        this.spaceTree.insertRight(leftTemp.getLeft(), neptune4);
        
        Planet neptune5 = new Neptune();
        super.addObject(neptune5, 580, 505);
        Cost cost33 = new Cost3();
        super.addObject(cost33, 555, 500);
        neptune5.setCost(cost33);
        this.spaceTree.insertLeft(leftTemp.getLeft(), neptune5);
        
        // Add Arrow to right child
        Arrow arrow20 = new Arrow();
        arrow20.getImage().rotate(-25);
        super.addObject(arrow20, 616, 490);
        
        // Add Arrow to left child
        Arrow arrow21 = new Arrow();
        arrow21.getImage().rotate(25);
        super.addObject(arrow21, 621, 519);
        
        //ReMap pointers once again...
        rightTemp = rightTemp.getLeft();
        leftTemp = leftTemp.getLeft();
        
        // Add 5th Tree Level
        Planet earth = new Earth();
        earth.setGoalPlanet(true);
        super.addObject(earth, 760, 125);
        Cost cost4 = new Cost4();
        super.addObject(cost4, 725, 115);
        earth.setCost(cost4);
        this.spaceTree.insertRight(rightTemp, earth);
        
        // More pointer Remapping......
        rightTemp = leftTemp.getParent().getRight();
        
        Planet earth2 = new Earth();
        earth2.setGoalPlanet(true);
        super.addObject(earth2, 760, 350);
        Cost cost5 = new Cost5();
        super.addObject(cost5, 725, 340);
        earth2.setCost(cost5);
        this.spaceTree.insertLeft(rightTemp.getRight(), earth2);
        
        Planet earth3 = new Earth();
        earth3.setGoalPlanet(true);
        super.addObject(earth3, 760, 450);
        Cost cost7 = new Cost7();
        super.addObject(cost7, 725, 440);
        earth3.setCost(cost7);
        this.spaceTree.insertRight(leftTemp.getLeft(), earth3);
        
        Planet earth4 = new Earth();
        earth4.setGoalPlanet(true);
        super.addObject(earth4, 760, 550);
        Cost cost92 = new Cost9();
        super.addObject(cost92, 725, 540);
        earth4.setCost(cost92);
        this.spaceTree.insertLeft(leftTemp.getLeft(), earth4);
    }
    
    /**
     * Prepare the world for the start of the program. That is: create the initial
     * objects and add them to the world.
     * <br /><br />
     * This is World #1.
     */
    private void createWorld1()
    {   // Add Salesman
        super.addObject(theSalesman, 20, 255);
        
        // Add Root or Tree
        Planet pluto = new Pluto();
        pluto.setCost(new Cost0());
        super.addObject(pluto, 26, 300);
   
         // Arrow to right child
        Arrow arrow = new Arrow();
        arrow.getImage().rotate(-55);
        super.addObject(arrow, 56, 248);
        
         // Arrow to left child
        Arrow arrow2 = new Arrow();
        arrow2.getImage().rotate(55);
        super.addObject(arrow2, 56, 348);
        
        this.spaceTree = new SpaceTree<Planet>(pluto);
        SpaceNode leftTemp = this.spaceTree.getRoot();
        SpaceNode rightTemp = this.spaceTree.getRoot();
        
        this.theSalesman.setCurrentPlanet(this.spaceTree.getRoot());
        
        // Add first tree level
        Planet neptune = new Neptune();
        super.addObject(neptune, 116, 161);
        Cost cost9 = new Cost9();
        super.addObject(cost9, 87, 164);
        neptune.setCost(cost9);
        rightTemp = this.spaceTree.insertRight(rightTemp, neptune);
        
        // Add arrow to right child
        Arrow arrow3 = new Arrow();
        arrow3.getImage().rotate(-42);
        super.addObject(arrow3, 166, 126);
        
        // Add arrow to left child
        Arrow arrow4 = new Arrow();
        arrow4.getImage().rotate(15);
        super.addObject(arrow4, 175, 182);
        
        Planet neptune1 = new Neptune();
        super.addObject(neptune1, 116, 433);
        Cost cost91 = new Cost9();
        super.addObject(cost91, 87, 434);
        neptune1.setCost(cost91);
        leftTemp = this.spaceTree.insertLeft(leftTemp, neptune1);
        
        // Add arrow to right child
        Arrow arrow5 = new Arrow();
        arrow5.getImage().rotate(-35);
        super.addObject(arrow5, 169, 399);
        
        // Add arrow to left child
        Arrow arrow6 = new Arrow();
        arrow6.getImage().rotate(48);
        super.addObject(arrow6, 167, 473);
        
        // Add second tree level
        Planet mars = new Mars();
        super.addObject(mars, 250, 86);
        Cost cost7 = new Cost7();
        super.addObject(cost7, 215, 77);
        mars.setCost(cost7);
        this.spaceTree.insertRight(rightTemp, mars);
        
        Planet mars2 = new Mars();
        super.addObject(mars2, 250, 200);
        Cost cost6 = new Cost6();
        super.addObject(cost6, 215, 190);
        mars2.setCost(cost6);
        this.spaceTree.insertLeft(rightTemp, mars2);
        
        // Add arrow right child
        Arrow arrow9 = new Arrow();
        arrow9.getImage().rotate(-45);
        super.addObject(arrow9, 324, 121);
        
        // Add arrow left child
        Arrow arrow10 = new Arrow();
        arrow10.getImage().rotate(-30);
        super.addObject(arrow10, 324, 176);
        
        Planet mars3 = new Mars();
        super.addObject(mars3, 250, 367);
        Cost cost71 = new Cost7();
        super.addObject(cost71, 215, 362);
        mars3.setCost(cost71);
        this.spaceTree.insertRight(leftTemp, mars3);
        
        // Add arrow right child
        Arrow arrow11 = new Arrow();
        arrow11.getImage().rotate(-45);
        super.addObject(arrow11, 316, 298);
        
        // Add arrow left child
        Arrow arrow12 = new Arrow();
        arrow12.getImage().rotate(-12);
        super.addObject(arrow12, 329, 360);
        
        Planet mars4 = new Mars();
        super.addObject(mars4, 250, 510);
        Cost cost8 = new Cost8();
        super.addObject(cost8, 215, 504);
        mars4.setCost(cost8);
        this.spaceTree.insertLeft(leftTemp, mars4);
        
        // Add arrow right child
        Arrow arrow13 = new Arrow();
        arrow13.getImage().rotate(-25);
        super.addObject(arrow13, 334, 471);
        
        // Add arrow left child
        Arrow arrow14 = new Arrow();
        arrow14.getImage().rotate(15);
        super.addObject(arrow14, 328, 525);
        
        // Add third tree level
        Planet moon = new Moon();
        super.addObject(moon, 415, 40);
        Cost cost4 = new Cost4();
        super.addObject(cost4, 390, 37);
        moon.setCost(cost4);
        this.spaceTree.insertRight(rightTemp.getLeft(), moon);
        
        Planet moon2 = new Moon();
        super.addObject(moon2, 415, 140);
        Cost cost3 = new Cost3();
        super.addObject(cost3, 390, 137);
        moon2.setCost(cost3);
        this.spaceTree.insertLeft(rightTemp.getLeft(), moon2);
        
        // Add arrow right child
        Arrow arrow15 = new Arrow();
        arrow15.getImage().rotate(-35);
        super.addObject(arrow15, 488, 90);
        
        // Add arrow left child
        Arrow arrow16 = new Arrow();
        arrow16.getImage().rotate(0);
        super.addObject(arrow16, 488, 143);
        
        Planet moon3 = new Moon();
        super.addObject(moon3, 415, 240);
        Cost cost31 = new Cost3();
        super.addObject(cost31, 390, 237);
        moon3.setCost(cost31);
        this.spaceTree.insertRight(leftTemp.getRight(), moon3);
        
        // Add arrow left child
        Arrow arrow17 = new Arrow();
        arrow17.getImage().rotate(10);
        super.addObject(arrow17, 488, 230);
        
        Planet moon4 = new Moon();
        super.addObject(moon4, 415, 340);
        Cost cost5 = new Cost5();
        super.addObject(cost5, 390, 337);
        moon4.setCost(cost5);
        this.spaceTree.insertLeft(leftTemp.getRight(), moon4);
        
         // Add arrow left child
        Arrow arrow18 = new Arrow();
        arrow18.getImage().rotate(10);
        super.addObject(arrow18, 491, 333);
        
        Planet moon5 = new Moon();
        super.addObject(moon5, 415, 440);
        Cost cost32 = new Cost3();
        super.addObject(cost32, 390, 437);
        moon5.setCost(cost32);
        this.spaceTree.insertRight(leftTemp.getLeft(), moon5);
        
        Planet moon6 = new Moon();
        super.addObject(moon6, 415, 540);
        Cost cost51 = new Cost5();
        super.addObject(cost51, 390, 537);
        moon6.setCost(cost51);
        this.spaceTree.insertLeft(leftTemp.getLeft(), moon6);
        
        // Add arrow left child
        Arrow arrow19 = new Arrow();
        arrow19.getImage().rotate(-30);
        super.addObject(arrow19, 490, 482);
        
        // Add arrow left child
        Arrow arrow20 = new Arrow();
        arrow20.getImage().rotate(0);
        super.addObject(arrow20, 498, 541);
        
        // Add fourth tree level
        Planet mercury = new Mercury();
        super.addObject(mercury, 580, 40);
        Cost cost33 = new Cost3();
        super.addObject(cost33, 550, 35);
        mercury.setCost(cost33);
        this.spaceTree.insertRight(rightTemp.getLeft().getLeft(), mercury);
        
        Planet mercury2 = new Mercury();
        super.addObject(mercury2, 580, 140);
        Cost cost41 = new Cost4();
        super.addObject(cost41, 550, 135);
        mercury2.setCost(cost41);
        this.spaceTree.insertLeft(rightTemp.getLeft().getLeft(), mercury2);
        
        // Arrow to right child
        Arrow arrow21 = new Arrow();
        arrow21.getImage().rotate(-15);
        super.addObject(arrow21, 661, 133);
        
        // Arrow to right child
        Arrow arrow22 = new Arrow();
        arrow22.getImage().rotate(27);
        super.addObject(arrow22, 657, 190);
        
        Planet mercury3 = new Mercury();
        super.addObject(mercury3, 580, 240);
        Cost cost2 = new Cost2();
        super.addObject(cost2, 550, 235);
        mercury3.setCost(cost2);
        this.spaceTree.insertLeft(leftTemp.getRight().getRight(), mercury3);
        
        // Arrow to right child
        Arrow arrow23 = new Arrow();
        arrow23.getImage().rotate(30);
        super.addObject(arrow23, 661, 297);
        
        Planet mercury4 = new Mercury();
        super.addObject(mercury4, 580, 340);
        Cost cost21 = new Cost2();
        super.addObject(cost21, 550, 335);
        mercury4.setCost(cost21);
        this.spaceTree.insertLeft(leftTemp.getRight().getLeft(), mercury4);
        
        Planet mercury5 = new Mercury();
        super.addObject(mercury5, 580, 440);
        Cost cost34 = new Cost3();
        super.addObject(cost34, 550, 435);
        mercury5.setCost(cost34);
        this.spaceTree.insertRight(leftTemp.getLeft().getLeft(), mercury5);
        
        // Arrow to right child
        Arrow arrow24 = new Arrow();
        arrow24.getImage().rotate(5);
        super.addObject(arrow24, 660, 442);
        
        // Arrow to right child
        Arrow arrow25 = new Arrow();
        arrow25.getImage().rotate(28);
        super.addObject(arrow25, 660, 499);
        
        Planet mercury6 = new Mercury();
        super.addObject(mercury6, 580, 540);
        Cost cost35 = new Cost3();
        super.addObject(cost35, 550, 537);
        mercury6.setCost(cost35);
        this.spaceTree.insertLeft(leftTemp.getLeft().getLeft(), mercury6);
        
        // Add final tree level
        Planet earth = new Earth();
        earth.setGoalPlanet(true);
        super.addObject(earth, 760, 125);
        Cost cost81 = new Cost8();
        super.addObject(cost81, 725, 115);
        earth.setCost(cost81);
        this.spaceTree.insertRight(rightTemp.getLeft().getLeft().getLeft(), earth);
        
        Planet earth2 = new Earth();
        earth2.setGoalPlanet(true);
        super.addObject(earth2, 760, 250);
        Cost cost52 = new Cost5();
        super.addObject(cost52, 725, 240);
        earth2.setCost(cost52);
        this.spaceTree.insertLeft(rightTemp.getLeft().getLeft().getLeft(), earth2);
        
        Planet earth3 = new Earth();
        earth3.setGoalPlanet(true);
        super.addObject(earth3, 760, 350);
        Cost cost72 = new Cost7();
        super.addObject(cost72, 725, 340);
        earth3.setCost(cost72);
        this.spaceTree.insertLeft(leftTemp.getRight().getRight().getLeft(), earth3);
        
        Planet earth4 = new Earth();
        earth4.setGoalPlanet(true);
        super.addObject(earth4, 760, 450);
        Cost cost61 = new Cost6();
        super.addObject(cost61, 725, 440);
        earth4.setCost(cost61);
        this.spaceTree.insertRight(leftTemp.getLeft().getLeft().getRight(), earth4);
        
        Planet earth5 = new Earth();
        earth5.setGoalPlanet(true);
        super.addObject(earth5, 760, 550);
        Cost cost62 = new Cost6();
        super.addObject(cost62, 725, 540);
        earth5.setCost(cost62);
        this.spaceTree.insertLeft(leftTemp.getLeft().getLeft().getRight(), earth5);
    }
    
    /**
     * Updates the Cost Display.
     */
    private void updateGameStatus()
    {
        Font font = new Font("Geneva", Font.PLAIN, 24);
        
        String text = "Cost: " + theSalesman.getTravelExpenses();
        
        Message cost = new Message(Color.WHITE, font, text, new Point(10, 35));
        
        if (this.costStatus != null) {
            super.removeObject(this.costStatus);
            this.costStatus = null;
        }
        
        this.costStatus = new GameStatus(cost, 130, 40);
        
        super.addObject(this.costStatus, 65, 575);
    }
    
    /**
     * Updates the Game Display.
     */
    private void updateGameTime()
    {
        Font font = new Font("Geneva", Font.PLAIN, 24);
        
        String text = "Time: " + ++this.timeElapsed;
        
        Message time = new Message(Color.WHITE, font, text, new Point(10, 35));
        
        if (this.timeStatus != null) {
            super.removeObject(this.timeStatus);
            this.timeStatus = null;
        }
        
        this.timeStatus = new GameStatus(time, 130, 40);
        
        super.addObject(this.timeStatus, 230, 575);
    }
    
    /**
     * Checks if the Timer has been started.
     * <br /><br />
     * Starts the Timer if it has not been started yet.
     */
    private void checkTimerStarted()
    {
        if (!this.theTimer.isRunning()) {
            this.theTimer.start();
        }
    }
    
    /**
     * Calculates the Players score.
     */
    private void calcScore()
    {
        int scorePart = this.theSalesman.getTravelExpenses() > SCORE_BASE ? 1 : SCORE_BASE - this.theSalesman.getTravelExpenses();
        int timePart = this.timeElapsed > TIME_BASE ? 1 : TIME_BASE - this.timeElapsed;
        int failPart = this.theSalesman.getSurplusExpenses() * 7;
        
        this.score = (scorePart * timePart) - failPart;
    }
    
    /**
     * Game Difficulty Setter.
     */
    private void setDifficulty(Difficulty difficulty)
    {
        this.difficulty = difficulty;
    }
    
    /**
     * Removes all MessageBoxes (and adherent Buttons) from the World.
     */
    private void removeMessageBoxes()
    {
        super.removeObjects(super.getObjects(MessageBox.class));
        super.removeObjects(super.getObjects(Button.class));
    }
    
    /**
     * Displays the Welcome Message.
     */
    private void displayWelcomeMessage()
    {
        // Generate Message
        MessageBox mb = new MessageBox();
        
        ArrayList<Message> contents = new ArrayList<Message>();
        
        Font font = new Font("Geneva", Font.PLAIN, 24);
        String text = "Welcome to The Greenfooted Salesman!";
        
        contents.add(new Message(Color.BLACK, font, text, new Point(75, 70)));
        
        text = "Your Goal is to reach 1 of the Terminal Planets,";
        
        contents.add(new Message(Color.BLACK, font, text, new Point(30, 150)));
        
        text = "but with minimising your travel expenses,";
        
        contents.add(new Message(Color.BLACK, font, text, new Point(30, 180)));
        
        text = "and with beating HAL...";
        
        contents.add(new Message(Color.BLACK, font, text, new Point(30, 210)));
        
        text = "...or you're sacked!!!";
        
        contents.add(new Message(Color.BLACK, font, text, new Point(175, 270)));
        
        text = "Please choose your difficulty:";
        
        contents.add(new Message(Color.BLACK, font, text, new Point(30, 350)));
        
        mb.drawMessage(contents);
        
        super.addObject(mb, 390, 285);
        
        // Generate Buttons
        Button btnEasy = new Button(ButtonType.DIFFICULTY);
        btnEasy.setImage("btnEasy.png");
        btnEasy.setDifficulty(Difficulty.EASY);
        btnEasy.addObserver(this);
        super.addObject(btnEasy, 230, 460);
        
        Button btnMedium = new Button(ButtonType.DIFFICULTY);
        btnMedium.setImage("btnMedium.png");
        btnMedium.setDifficulty(Difficulty.MEDIUM);
        btnMedium.addObserver(this);
        super.addObject(btnMedium, 390, 460);
        
        Button btnHard = new Button(ButtonType.DIFFICULTY);
        btnHard.setImage("btnHard.png");
        btnHard.setDifficulty(Difficulty.HARD);
        btnHard.addObserver(this);
        super.addObject(btnHard, 550, 460);  
    }
    
    /**
     * Displays the Game End Message.
     */
    private void displayGameEndMessage()
    {
        boolean playerWins = this.halScore >= this.theSalesman.getTravelExpenses();
        
        // Generate Message
        MessageBox mb = new MessageBox();
        
        ArrayList<Message> contents = new ArrayList<Message>();
        
        Font font = new Font("Geneva", Font.PLAIN, 24);
        String text = null;
        Point p = null;
        
        if (playerWins) {
            text = "Congratulations - you WIN!!!";
            
            contents.add(new Message(Color.BLACK, font, text, new Point(130, 70)));
            
            text = "You've beaten HAL in reaching your goal,";
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 120)));
            
            text = "so you're not going to be sacked (yet)!";
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 150)));
            
            text = "Your travel expenses: " + this.theSalesman.getTravelExpenses();
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 200)));
            
            text = "HALs travel expenses: " + this.halScore;
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 230)));
            
            text = "Your Score: " + this.score;
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 280)));
            
            text = "Wanna go on another tour?";
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 330)));
        } else {
            text = "Oh no, you FAIL!!!!";
            
            contents.add(new Message(Color.BLACK, font, text, new Point(200, 70)));
            
            text = "HAL has beaten you!"; 
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 120)));
            
            text = "So we're going to give him your job.";
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 150)));
            
            text = "Your travel expenses: " + this.theSalesman.getTravelExpenses();
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 200)));
            
            text = "HALs travel expenses: " + this.halScore;
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 230)));
            
            text = "Your Score: " + this.score;
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 280)));
            
            text = "But you may have 1 last chance (if you want).";
            
            contents.add(new Message(Color.BLACK, font, text, new Point(30, 330)));
            
        }
        
        mb.drawMessage(contents);
        
        super.addObject(mb, 390, 285);
        
        // Generate Buttons
        Button btnYes = new Button(ButtonType.YES);
        btnYes.setImage("btnYes.png");
        btnYes.addObserver(this);
        super.addObject(btnYes, 310, 460);
        
        Button btnNo = new Button(ButtonType.NO);
        btnNo.setImage("btnNo.png");
        btnNo.addObserver(this);
        super.addObject(btnNo, 470, 460);
        
        
    }
    
    /**
     * Resets the World.
     * <br /><br />
     * Flushes all execution control variables.
     */
    private void resetWorld()
    {
        this.spaceTree = null;
        this.costStatus = null;
        this.timeStatus = null;
        this.theTimer.stop();
        this.timeElapsed = -1;
        this.score = 0;
        this.halScore = Integer.MAX_VALUE;
        
        this.theSalesman.resetSalesman();
        
        super.removeObjects(super.getObjects(Actor.class));
        this.removeMessageBoxes();
        
        this.createRandomWorld();
        
        this.updateGameStatus();
        this.updateGameTime();
        this.displayWelcomeMessage();
    }
}
